/****************************************************************
 ** Product  :   HP Subscriber Network Application Policy
 ** Module   :   gr-manager
 ** Date: Apr 10, 2015               
 ** Author: Huang xiaoji
 ** (C) Copyright 2015, Hewlett-Packard Company, All Rights Reserved.
 ** This software is the proprietary information of HP, Inc.  
 ** Use is subject to license terms.
 *****************************************************************
 ** Description:   
 **   SNMPTrapHelper class as a trap-directed notification tool, it encapsulate a number of methods to help sending trap notification.
 **  
 ****************************************************************/
package com.hp.snap.gr.utils;

import com.hp.snap.gr.exception.ActionException;
import com.hp.snap.gr.model.jaxb.SNMPTrapContentType;
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;

/**
 * SNMPTrapHelper class as a trap-directed notification tool, it encapsulate a number of methods to help sending trap notification.
 *
 * <P>
 * Version: 3.3 <br>
 * Author: Huang xiaoji
 * Date: Apr 10, 2015
 * 
 * </P>
 *
 **/
public class SNMPTrapHelper {
    public static final String SNMP_TRAP_DESTINATION = "SNAP_ALARM_RECEIVERS";
    public static String NET_SNMP_COMMAND = "snmptrap";
    public static String SNAP_ENTERPRISE_OID = "";
    public static String TRAP_MESSAGE_OID = "";
    public static long startUpTime = 0;
    private static Logger logger = LoggerFactory.getLogger(SNMPTrapHelper.class);

    public static void sendTrap(String messageText) throws ActionException {
        if (logger.isTraceEnabled()) {
            logger.trace("going to send snmp trap with message: {}", messageText);
        }
        if (startUpTime == 0) startUpTime = System.currentTimeMillis();

        SNAP_ENTERPRISE_OID = Utils.getConfigurationValue(Constants.CFG_SNAP_ENTERPRISE_OID);
        TRAP_MESSAGE_OID = Utils.getConfigurationValue(Constants.CFG_TRAP_MESSAGE_OID);

        String[] destinations = Utils.getMultiConfigurationValue(Constants.CFG_GRG_ROOT, SNMP_TRAP_DESTINATION);
        if (logger.isTraceEnabled()){
            logger.trace("going to send snmp trap to {}", ArrayUtils.toString(destinations, "UN-CONFIGURED"));
        }
        if (destinations != null && ArrayUtils.isNotEmpty(destinations)) {
            SNMPTrapContentType content = new SNMPTrapContentType();
            content.setOid(TRAP_MESSAGE_OID);
            content.setType("string");
            content.setVal(messageText);
            List<SNMPTrapContentType> contents = new LinkedList<SNMPTrapContentType>();
            contents.add(content);

            for (String destination : destinations) {
                destinationObject dst = getDestination(destination);
                sendTrap(SNAP_ENTERPRISE_OID, dst, contents);
            }
        }
    }

    private static String buildNetSNMPCommand(String enterpriseOid, destinationObject destination, List<SNMPTrapContentType> contents) {
        //build command line string
        //snmptrap -v 1 [COMMON OPTIONS] [-Ci] enterprise-oid agent generic-trap specific-trap uptime [OID TYPE VALUE]...
        //snmptrap -v [2c|3] [COMMON OPTIONS] [-Ci] uptime trap-oid [OID TYPE VALUE]...

        StringBuffer sb = new StringBuffer(NET_SNMP_COMMAND);
        if (destination.version.equalsIgnoreCase("V1")) {
            sb.append(" -v 1 -c ").append(destination.community).append(" ").append(destination.ipAddress).append(":").append(destination.port).append(" ")
                    .append(enterpriseOid).append(" \"\" 6 99 ").append(System.currentTimeMillis() - startUpTime);
            for (SNMPTrapContentType content : contents) {
                sb.append(" ").append(content.getOid()).append(" ").append(content.getType()).append(" \"").append(content.getVal()).append("\"");
            }
        } else {
            sb.append(" -v ");
            if (destination.version.equalsIgnoreCase("V2C")) {
                sb.append("2c");
            } else {
                sb.append("3");
            }
            sb.append(" -c ").append(destination.community).append(" ").append(destination.ipAddress).append(":").append(destination.port).append(" ")
                    .append(System.currentTimeMillis() - startUpTime)
                    .append(" ").append(enterpriseOid);
            for (SNMPTrapContentType content : contents) {
                sb.append(" ").append(content.getOid()).append(" ").append(content.getType()).append(" \"")
                        .append(content.getVal()).append("\"");
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("executing command line: {}", sb);
        }
        return sb.toString();
    }

    private static int parseWithDefault(String number, int defaultVal) {
        try {
            return Integer.parseInt(number);
        } catch (NumberFormatException e) {
            return defaultVal;
        }
    }

    public static void sendTrap(String enterpriseOid, List<String> destination, List<SNMPTrapContentType> content) {
        if (destination == null || destination.isEmpty()) {
            logger.warn("destination of SNMPTrap is missing");
            return;
        }
        if (content == null || content.isEmpty()) {
            logger.warn("message content of SNMPTrap is missing");
            return;
        }

        for (String dst : destination) {
            destinationObject dstObj = getDestination(dst);
            if (dstObj != null) {
                sendTrap(enterpriseOid, dstObj, content);
            }
        }
    }

    private static void sendTrap(String enterpriseOid, destinationObject dstObj, List<SNMPTrapContentType> content) {
        try {
            String cmd = buildNetSNMPCommand(enterpriseOid, dstObj, content);
            int result = CmdUtils.runShellScript(cmd);

            if (result != 0) {
                logger.warn("execute command failed, result code " + result);
            } else {
                if (logger.isTraceEnabled()) {
                    logger.trace("snmp trap send successful.");
                }
            }
        } catch (Exception e) {
            logger.warn("execute command failed, " + e.getMessage(), e);
        }
    }

    private static destinationObject getDestination(String destination) {
        String[] vars = destination.split("\\s*:\\s*");
        if (vars.length < 2) {
            logger.warn("can't parse trap destination from parameter: " + destination);
            return null;
        }
        destinationObject ret = new destinationObject();
        ret.ipAddress = vars[0];
        ret.port = 162;
        ret.version = "V2C";
        ret.community = "public";

        if (vars.length > 2) {
            if (vars.length > 3) {
                ret.port = parseWithDefault(vars[2], 162);
                ret.version = vars[1];
            } else if (vars[1].equalsIgnoreCase("V1") || vars[1].equalsIgnoreCase("V2C") || vars[1].equalsIgnoreCase("V3")) {
                ret.version = vars[1];
            } else {
                ret.port = parseWithDefault(vars[1], 162);
            }
        }
        ret.community = vars[vars.length - 1];

        if (logger.isDebugEnabled()) {
            logger.debug("going to send trap to {}:{} version: {} community: {}",
                    new Object[]{ret.ipAddress, ret.port, ret.version, ret.community});
        }
        return ret;
    }

    static class destinationObject {
        String ipAddress, version, community;
        int port;
    }
}